package com.aizuda.boot.modules.business.job;

import com.aizuda.boot.modules.business.contract.entity.ContractFieldEntity;
import com.aizuda.boot.modules.business.contract.entity.ContractFieldValueEntity;
import com.aizuda.boot.modules.business.contract.mapper.ContractFieldValueMapper;
import com.aizuda.boot.modules.business.contract.entity.ContractEntity;
import com.aizuda.boot.modules.business.contract.service.ContractFieldService;
import com.aizuda.boot.modules.business.contract.service.ContractService;
import com.aizuda.boot.modules.business.performance.domain.entity.ContractPerformanceTypeEntity;
import com.aizuda.boot.modules.common.constant.enums.PerformanceObjectEnum;
import com.aizuda.boot.modules.common.constant.enums.PerformanceTimeEnum;
import com.aizuda.boot.modules.business.performance.service.ContractPerformanceService;
import com.aizuda.boot.modules.business.performance.service.ContractPerformanceTypeService;
import com.aizuda.boot.modules.common.MessageEvent;
import com.aizuda.boot.modules.system.entity.SysDepartment;
import com.aizuda.boot.modules.system.entity.SysUserDepartment;
import com.aizuda.boot.modules.system.entity.enums.BusinessType;
import com.aizuda.boot.modules.system.entity.vo.department.DepartmentInfoDTO;
import com.aizuda.boot.modules.system.service.ISysDepartmentService;
import com.aizuda.boot.modules.system.service.ISysUserDepartmentService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author sangyirong
 * @ClassName PerformanceJob
 * @Version 1.0
 * @Date 2025/7/9 16:34
 */
@Slf4j
@Component
public class PerformanceJob {


    private static final String PAYMENT_TIME = "payment_time";

    private static final String RECEIVAL_TIME = "receival_time";

    private static final String CONTRACT_TYPE = "contract_type";

    @Resource
    private ContractPerformanceService contractPerformanceService;

    @Resource
    private ContractPerformanceTypeService contractPerformanceTypeService;

    @Resource
    private ContractService contractService;

    @Resource
    private ContractFieldValueMapper contractFieldValueMapper;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private ISysDepartmentService sysDepartmentService;

    @Resource
    private ISysUserDepartmentService sysUserDepartmentService;

    @Resource
    private ContractFieldService contractfieldservice;

    @XxlJob("sendPerformanceMessageHandler")
    public void sendPerformanceMessage() {
        log.info("开始执行合同履约任务");
        contractPerformanceService.list().forEach(performance -> {
            try {
                // 获取履约规则关联的合同类型ID列表
                List<Integer> typeIds = contractPerformanceTypeService.list(Wrappers.<ContractPerformanceTypeEntity>lambdaQuery()
                                .eq(ContractPerformanceTypeEntity::getPerformanceId, performance.getId())
                                .select(ContractPerformanceTypeEntity::getContractTypeId))
                        .stream().map(ContractPerformanceTypeEntity::getContractTypeId).collect(Collectors.toList());

                if (CollectionUtils.isEmpty(typeIds)) {
                    log.debug("履约规则 {} 没有关联的合同类型，跳过", performance.getId());
                    return;
                }

                // 获取合同类型字段id
                ContractFieldEntity contractTypeField = contractfieldservice.getOne(Wrappers.<ContractFieldEntity>lambdaQuery()
                        .eq(ContractFieldEntity::getCode, CONTRACT_TYPE));

                // 根据合同类型获取关联的合同ID列表
                List<Integer> contractIds = contractFieldValueMapper.selectList(Wrappers.<ContractFieldValueEntity>lambdaQuery()
                                .eq(ContractFieldValueEntity::getFieldId, contractTypeField.getId()) // 合同类型字段ID
                                .in(ContractFieldValueEntity::getFieldValue, typeIds)
                                .select(ContractFieldValueEntity::getContractId))
                        .stream().map(ContractFieldValueEntity::getContractId).collect(Collectors.toList());

                if (CollectionUtils.isEmpty(contractIds)) {
                    log.debug("履约规则 {} 没有关联的合同，跳过", performance.getId());
                    return;
                }

                // 过滤掉草稿状态的合同
                List<ContractEntity> validContracts = contractService.list(Wrappers.<ContractEntity>lambdaQuery()
                        .in(ContractEntity::getId, contractIds)
                        .ne(ContractEntity::getCurrentStatus, "DRAFT") // 排除草稿状态
                        .select(ContractEntity::getId));

                List<Integer> validContractIds = validContracts.stream()
                        .map(ContractEntity::getId)
                        .collect(Collectors.toList());

                if (CollectionUtils.isEmpty(validContractIds)) {
                    log.debug("履约规则 {} 过滤草稿后没有有效合同，跳过", performance.getId());
                    return;
                }

                // 获取付款时间和收款时间字段ID
                List<Long> list = contractfieldservice.list(Wrappers.<ContractFieldEntity>lambdaQuery()
                                .in(ContractFieldEntity::getCode, PAYMENT_TIME, RECEIVAL_TIME)
                                .select(ContractFieldEntity::getId, ContractFieldEntity::getCode))
                        .stream().map(ContractFieldEntity::getId).toList();

                // 273 --> 付款时间  278 --> 收款时间
                // 获取合同到期时间映射
                Map<Integer, String> contractEndDateMap = contractFieldValueMapper.selectList(Wrappers.<ContractFieldValueEntity>lambdaQuery()
                                .in(ContractFieldValueEntity::getFieldId, list) // 合同期限（结束日期）字段ID
                                .in(ContractFieldValueEntity::getContractId, validContractIds)
                                .select(ContractFieldValueEntity::getContractId, ContractFieldValueEntity::getFieldValue))
                        .stream().collect(Collectors.toMap(ContractFieldValueEntity::getContractId, ContractFieldValueEntity::getFieldValue));

                if (CollectionUtils.isEmpty(contractEndDateMap)) {
                    log.debug("履约规则 {} 没有关联到期的合同，跳过", performance.getId());
                    return;
                }

                // 解析提醒时间配置
                List<PerformanceTimeEnum> performanceTimeList = parsePerformanceTime(performance.getPerformanceTime());
                if (CollectionUtils.isEmpty(performanceTimeList)) {
                    log.warn("履约规则 {} 的提醒时间配置无效，跳过", performance.getId());
                    return;
                }

                // 当前日期
                LocalDate today = LocalDate.now();

                // 筛选需要提醒的合同
                List<Integer> contractsToRemind = new ArrayList<>();

                for (Map.Entry<Integer, String> entry : contractEndDateMap.entrySet()) {
                    Integer contractId = entry.getKey();
                    String endDateStr = entry.getValue();

                    try {
                        LocalDate endDate = parseDate(endDateStr);
                        if (endDate == null) {
                            log.warn("合同 {} 的到期时间格式无效: {}", contractId, endDateStr);
                            continue;
                        }

                        boolean shouldRemind = false;

                        // 检查是否符合提醒条件
                        for (PerformanceTimeEnum timeEnum : performanceTimeList) {
                            if (PerformanceTimeEnum.ON_DUE_DATE.equals(timeEnum)) {
                                // 到期当天提醒
                                if (today.equals(endDate)) {
                                    shouldRemind = true;
                                    break;
                                }
                            } else if (PerformanceTimeEnum.BEFORE_DUE_DATE.equals(timeEnum)) {
                                // 到期前提醒
                                Integer performanceDay = performance.getPerformanceDay();
                                if (performanceDay != null && performanceDay > 0) {
                                    LocalDate reminderDate = endDate.minusDays(performanceDay);
                                    if (today.equals(reminderDate)) {
                                        shouldRemind = true;
                                        break;
                                    }
                                }
                            }
                        }

                        if (shouldRemind) {
                            contractsToRemind.add(contractId);
                        }

                    } catch (Exception e) {
                        log.warn("处理合同 {} 到期时间时发生错误: {}", contractId, e.getMessage());
                    }
                }

                if (!contractsToRemind.isEmpty()) {
                    log.info("履约规则 {} 找到 {} 个需要提醒的合同", performance.getPerformanceName(), contractsToRemind.size());

                    // 发送提醒消息
                    for (Integer contractId : contractsToRemind) {
                        sendPerformanceReminder(performance, contractId, contractEndDateMap.get(contractId));
                    }
                } else {
                    log.debug("履约规则 {} 没有找到需要提醒的合同", performance.getPerformanceName());
                }

            } catch (Exception e) {
                log.error("处理履约规则 {} 时发生错误", performance.getId(), e);
            }
        });

        log.info("结束执行合同履约任务");
    }

    /**
     * 解析提醒时间配置
     */
    private List<PerformanceTimeEnum> parsePerformanceTime(String performanceTimeStr) {
        if (performanceTimeStr == null || performanceTimeStr.trim().isEmpty()) {
            return Collections.emptyList();
        }

        return Arrays.stream(performanceTimeStr.split(","))
                .map(String::trim)
                .map(timeCode -> {
                    try {
                        return PerformanceTimeEnum.valueOf(timeCode);
                    } catch (IllegalArgumentException e) {
                        log.warn("无效的提醒时间代码: {}", timeCode);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 解析日期字符串
     */
    private LocalDate parseDate(String dateStr) {
        if (dateStr == null || dateStr.trim().isEmpty()) {
            return null;
        }

        try {
            // 只处理 yyyy-MM-dd HH:mm:ss 格式，只取日期部分
            return LocalDate.parse(dateStr.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        } catch (Exception e) {
            log.warn("日期格式解析失败: {}", dateStr);
            return null;
        }
    }

    /**
     * 发送履约提醒消息
     */
    private void sendPerformanceReminder(com.aizuda.boot.modules.business.performance.domain.entity.ContractPerformanceEntity performance,
                                         Integer contractId, String endDate) {
        try {
            // 获取需要通知的人员列表
            List<Long> userIds = getNotificationUsers(performance, contractId);

            if (userIds.isEmpty()) {
                log.warn("合同 {} 未找到需要通知的人员", contractId);
                return;
            }

            // 获取合同名称
            String contractName = getContractFieldValue(contractId, 5L); // 合同名称字段ID为5
            if (contractName == null) {
                contractName = "未知合同";
            }

            // 构建消息内容
            String title = "合同履约提醒";
            String content = buildReminderContent(performance, contractName, endDate);

            // 发送消息事件
            MessageEvent messageEvent = new MessageEvent();
            messageEvent.setTitle(title);
            messageEvent.setContent(content);
            messageEvent.setCreateId(0L);
            messageEvent.setCreateBy("系统");
            messageEvent.setCategory(2); // 2表示待办消息
            messageEvent.setBusinessId(contractId.longValue());
            messageEvent.setBusinessType(BusinessType.performance.name());
            messageEvent.setUserIds(userIds);

            applicationEventPublisher.publishEvent(messageEvent);

            log.info("成功发送履约提醒消息：合同 {} - {} 给 {} 个用户", contractId, contractName, userIds.size());

        } catch (Exception e) {
            log.error("发送合同 {} 履约提醒消息失败", contractId, e);
        }
    }

    /**
     * 构建提醒消息内容
     */
    private String buildReminderContent(com.aizuda.boot.modules.business.performance.domain.entity.ContractPerformanceEntity performance,
                                        String contractName, String endDate) {
        StringBuilder content = new StringBuilder();
        content.append("合同【").append(contractName).append("】");

        // 根据履约类型构建不同的消息内容
        switch (performance.getPerformanceType()) {
            case DUE_SOON:
                content.append("即将到期，到期时间：").append(endDate);
                break;
            case DUE_DATE_REMINDER:
                content.append("付费提醒，到期时间：").append(endDate);
                break;
            case COLLECTION_DATE_REMINDER:
                content.append("收款提醒，到期时间：").append(endDate);
                break;
            default:
                content.append("履约提醒，到期时间：").append(endDate);
                break;
        }

        content.append("，请及时处理相关事务。");
        return content.toString();
    }

    /**
     * 获取需要通知的人员列表
     */
    private List<Long> getNotificationUsers(com.aizuda.boot.modules.business.performance.domain.entity.ContractPerformanceEntity performance,
                                            Integer contractId) {
        List<Long> userIds = new ArrayList<>();

        // 解析提醒对象配置
        List<PerformanceObjectEnum> performanceObjects = parsePerformanceObject(performance.getPerformanceObject());

        for (PerformanceObjectEnum objectEnum : performanceObjects) {
            switch (objectEnum) {
                case APPLICANT:
                    // 获取合同申请人
                    List<Long> applicants = getContractFieldUsers(contractId, 2L); // 申请人字段ID为2
                    userIds.addAll(applicants);
                    break;
                case HANDLER:
                    // 获取合同经办人  
                    List<Long> handlers = getContractFieldUsers(contractId, 28L); // 合同经办人字段ID为28
                    userIds.addAll(handlers);
                    break;
                case SPECIFIED_PERSON:
                    // 获取指定人员
                    List<Long> specifiedUsers = parseSpecifiedUsers(performance.getPerformanceUser());
                    userIds.addAll(specifiedUsers);
                    break;
                case APPLICANT_SUPERVISOR:
                    Long applicant = getContractFieldUsers(contractId, 2L).get(0); // 申请人字段ID为2
                    if (null == applicant) {
                        break;
                    }
                    DepartmentInfoDTO main = sysDepartmentService.getEmployeeMainDepartmentForCompatibility(applicant);
                    SysDepartment department = sysDepartmentService.getById(main.getId());
                    if (null != department || null != department.getHeadId()) {
                        userIds.add(department.getHeadId());
                    }
                    break;
                case LEGAL_COUNSEL:
                    SysDepartment legal = sysDepartmentService.getOne(Wrappers.<SysDepartment>lambdaQuery()
                            .eq(SysDepartment::getCode, "2a")
                            .last("limit 1"));
                    userIds.addAll(sysUserDepartmentService.list(Wrappers.<SysUserDepartment>lambdaQuery()
                                    .eq(SysUserDepartment::getDepartmentId, legal.getId()))
                            .stream().map(SysUserDepartment::getUserId).collect(Collectors.toList()));
                    break;
                default:
                    log.warn("未知的提醒对象类型: {}", objectEnum);
                    break;
            }
        }

        // 去重并过滤无效用户ID
        return userIds.stream()
                .filter(Objects::nonNull)
                .filter(userId -> userId > 0)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 解析提醒对象配置
     */
    private List<PerformanceObjectEnum> parsePerformanceObject(String performanceObjectStr) {
        if (performanceObjectStr == null || performanceObjectStr.trim().isEmpty()) {
            return Collections.emptyList();
        }

        return Arrays.stream(performanceObjectStr.split(","))
                .map(String::trim)
                .map(objectCode -> {
                    try {
                        return PerformanceObjectEnum.valueOf(objectCode);
                    } catch (IllegalArgumentException e) {
                        log.warn("无效的提醒对象代码: {}", objectCode);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 解析指定用户列表
     */
    private List<Long> parseSpecifiedUsers(String performanceUserStr) {
        if (performanceUserStr == null || performanceUserStr.trim().isEmpty()) {
            return Collections.emptyList();
        }

        return Arrays.stream(performanceUserStr.split(","))
                .map(String::trim)
                .map(userIdStr -> {
                    try {
                        return Long.valueOf(userIdStr);
                    } catch (NumberFormatException e) {
                        log.warn("无效的用户ID: {}", userIdStr);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 获取合同字段中的用户列表
     */
    private List<Long> getContractFieldUsers(Integer contractId, Long fieldId) {
        try {
            List<ContractFieldValueEntity> fieldValues = contractFieldValueMapper.selectList(
                    Wrappers.<ContractFieldValueEntity>lambdaQuery()
                            .eq(ContractFieldValueEntity::getContractId, contractId)
                            .eq(ContractFieldValueEntity::getFieldId, fieldId)
                            .select(ContractFieldValueEntity::getFieldValue)
            );

            return fieldValues.stream()
                    .map(ContractFieldValueEntity::getFieldValue)
                    .filter(Objects::nonNull)
                    .filter(value -> !value.trim().isEmpty())
                    .flatMap(value -> Arrays.stream(value.split(","))
                            .map(String::trim)
                            .map(userIdStr -> {
                                try {
                                    return Long.valueOf(userIdStr);
                                } catch (NumberFormatException e) {
                                    log.warn("字段值中包含无效的用户ID: {}", userIdStr);
                                    return null;
                                }
                            })
                            .filter(Objects::nonNull))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取合同 {} 字段 {} 的用户列表失败", contractId, fieldId, e);
            return Collections.emptyList();
        }
    }

    /**
     * 获取合同字段值
     */
    private String getContractFieldValue(Integer contractId, Long fieldId) {
        try {
            List<ContractFieldValueEntity> fieldValues = contractFieldValueMapper.selectList(
                    Wrappers.<ContractFieldValueEntity>lambdaQuery()
                            .eq(ContractFieldValueEntity::getContractId, contractId)
                            .eq(ContractFieldValueEntity::getFieldId, fieldId)
                            .select(ContractFieldValueEntity::getFieldValue)
                            .last("LIMIT 1")
            );

            return fieldValues.isEmpty() ? null : fieldValues.get(0).getFieldValue();
        } catch (Exception e) {
            log.error("获取合同 {} 字段 {} 的值失败", contractId, fieldId, e);
            return null;
        }
    }
}
